/* File: dcodrle4.c
   Author: David Bourgin
   Creation date: 1/2/94
   Last update: 22/9/94
   Purpose: Example of RLE type 4 decoding with a file source to decompress.
*/

#include <stdio.h>
/* For routines printf,fgetc,fread and fwrite */
#include <memory.h>
/* For routines memset,memcpy */
#include <stdlib.h>
/* For routine exit */

/* Error codes sent to the caller */
#define NO_ERROR      0
#define BAD_FILE_NAME 1
#define BAD_ARGUMENT  2

/* Useful constants */
#define FALSE 0
#define TRUE  1

/* Global variables */
FILE *source_file,*dest_file;

                             /* Being that fgetc=EOF only after an access
                                then 'byte_stored_status' is 'TRUE' if a byte has been stored by 'fgetc'
                                or 'FALSE' if there's no valid byte not already read and not handled in 'val_byte_stored' */
int byte_stored_status=FALSE;
int val_byte_stored;

/* Pseudo procedures */
#define end_of_data()  (byte_stored_status?FALSE:!(byte_stored_status=((val_byte_stored=fgetc(source_file))!=EOF)))
#define read_byte()  (byte_stored_status?byte_stored_status=FALSE,(unsigned char)val_byte_stored:(unsigned char)fgetc(source_file))
#define read_array(array,nb_to_read)  { if (byte_stored_status)\
                                          { *(array)=(unsigned char)val_byte_stored;\
                                            byte_stored_status=FALSE;\
                                            if ((nb_to_read)>1)\
                                               (void)fread(&((array)[1]),1,(nb_to_read)-1,source_file);\
                                          }\
                                       else (void)fread((array),1,(nb_to_read),source_file);\
                                     }
#define write_array(array,array_size)  ((void)fwrite((array),1,(array_size),dest_file))
#define fill_array(array,nb_to_fill,value)  ((void)memset((array),(value),(nb_to_fill)))
#define block_copy(source,destination,source_size,nb_of_time)  { register unsigned int i, index=0;\
                                                                 for (i=1;i<=(nb_of_time);i++)\
                                                                     { (void)memcpy(&((destination)[index]),(source),(source_size));\
                                                                       index += (source_size);\
                                                                     }\
                                                               }

void rle4decoding()
/* Returned parameters: None
   Action: Decompresses with RLE type 4 method all bytes read by the function read_byte
   Erreurs: An input/output error could disturb the running of the program
*/
{ unsigned char byte_code;
  unsigned int frame_size,frame_nb;
  unsigned char frame[16705];

  while (!end_of_data())
        { byte_code=read_byte();
          switch (byte_code & 192)
          { case 0:          /* Frames repetition of 1 byte
                                Encoding [00xxxxxx|1 byte] */
                   frame_size=(byte_code & 63)+2;
                   fill_array(frame,frame_size,read_byte());
                   break;
            case 64:         /* Frames repetition of less 66 bytes
                                Encoding [01xxxxxx|xxxxxxxx|1 byte] */
                    frame_size=((byte_code & 63) << 8)+read_byte()+66;
                    fill_array(frame,frame_size,read_byte());
                    break;
            case 128:        /* Frame with several bytes
                                Encoding [10xxxxxx|yyyyyyyy|n bytes] */
                     frame_size=(byte_code & 63)+2;
                     frame_nb=read_byte()+2;
                     read_array(frame,frame_size);
                     block_copy(frame,&(frame[frame_size]),frame_size,frame_nb);
                     frame_size *= frame_nb;
                     break;
            case 192:        /* No repetition
                                Encoding [110xxxxxx|n octets] or [111xxxxxx|yyyyyyyy|n octets] */
                     frame_size=byte_code & 31;
                     if (!(byte_code & 32))
                             /* Non repetition of less 33 bytes [110xxxxxx|n bytes] ? */
                        frame_size++;
                     else frame_size=(frame_size << 8)+read_byte()+33;
                     read_array(frame,frame_size);
                     break;
          }
          write_array(frame,frame_size);
        }
}

void aide()
/* Returned parameters: None
   Action: Displays the help of the program and the stop its running
   Erreurs: None
*/
{ printf("This utility enables you to decompress a file by using RLE type 4 method\n");
  printf("as given in 'La Video et Les Imprimantes sur PC'\n");
  printf("\nUse: dcodrle4 source target\n");
  printf("source: Name of the file to decompress\n");
  printf("target: Name of the restored file\n");
}

int main(argc,argv)
/* Returned parameters: Returns an error code (0=None)
   Action: Main procedure
   Erreurs: Detected, handled and an error code is returned, if any
*/
int argc;
char *argv[];
{ if (argc!=3)
     { aide();
       exit(BAD_ARGUMENT);
     }
  else if ((source_file=fopen(argv[1],"rb"))==NULL)
          { aide();
            exit(BAD_FILE_NAME);
          }
       else if ((dest_file=fopen(argv[2],"wb"))==NULL)
               { aide();
                 exit(BAD_FILE_NAME);
               }
            else { rle4decoding();
                   fclose(source_file);
                   fclose(dest_file);
                 }
  printf("Execution of dcodrle4 completed.\n");
  return (NO_ERROR);
}
